﻿using System;
using System.ComponentModel;
using System.Globalization;
using System.Net;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;

namespace IpControlsWpf
{
    public delegate void IPv6AddressChangedEventHandler(object sender, EventArgs e);

    /// <summary>
    /// Interaktionslogik für IPv6TextBox.xaml
    /// </summary>
    public partial class IPv6TextBox : UserControl
    {
        private String _tmp;

        public static readonly DependencyProperty AddressProperty = DependencyProperty.Register("Address", typeof(IPAddress), typeof(IPv6TextBox), new UIPropertyMetadata(IPAddress.IPv6None, new PropertyChangedCallback(AddressPropertyChanged)));

        /// <summary>
        /// Gets or sets the contained and visible IPv6 address of the control (default = ::).
        /// </summary>
        [Browsable(false)]
        public IPAddress Address
        {
            get
            {
                return (IPAddress)(GetValue(AddressProperty));
            }
            set
            {
                SetValue(AddressProperty, value);
            }
        }

        private static void AddressPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            IPv6TextBox target = (IPv6TextBox)d;

            if (target != null)
            {
                target.SetIPAddress((IPAddress)e.NewValue);
            }
        }

        public event IPv6AddressChangedEventHandler AddressChanged;

        public IPv6TextBox()
        {
            InitializeComponent();

            _tmp = String.Empty;
        }

        private delegate void SetIPAddressDelegate(IPAddress value);
        private void SetIPAddress(IPAddress value)
        {
            if (Dispatcher.Thread == Thread.CurrentThread)
            {
                String valueStr;

                if (value.ToString().Contains("."))
                {
                    String[] hybrid = value.ToString().Split(new Char[] { ':' }, 7);
                    String[] ipv4Appendix = hybrid[6].Split(new Char[] { '.' }, 4);

                    valueStr = hybrid[0] + ":" + hybrid[1] + ":" +
                                hybrid[2] + ":" + hybrid[3] + ":" +
                                hybrid[4] + ":" + hybrid[5] + ":" +
                                Int32.Parse(ipv4Appendix[0]).ToString("X2").ToLowerInvariant() +
                                Int32.Parse(ipv4Appendix[1]).ToString("X2").ToLowerInvariant() + ":" +
                                Int32.Parse(ipv4Appendix[2]).ToString("X2").ToLowerInvariant() +
                                Int32.Parse(ipv4Appendix[3]).ToString("X2").ToLowerInvariant();
                }
                else
                {
                    valueStr = value.ToString();
                }

                String[] doctets = new String[8];
                String[] token = valueStr.Split(new Char[] { ':' }, 8);

                for (Int32 i = 0; i < token.Length; i++)
                {
                    if (token[i] == String.Empty) break;
                    doctets[i] = token[i];
                }

                Int32 j = 7;
                for (Int32 i = token.Length - 1; i > 0; i--)
                {
                    if (token[i] == String.Empty) break;
                    doctets[j--] = token[i];
                }

                for (Int32 i = 0; i < doctets.Length; i++)
                {
                    if (doctets[i] == null || doctets[i] == String.Empty) doctets[i] = "0";
                }

                tB_DOctet0.Text = doctets[0];
                tB_DOctet1.Text = doctets[1];
                tB_DOctet2.Text = doctets[2];
                tB_DOctet3.Text = doctets[3];
                tB_DOctet4.Text = doctets[4];
                tB_DOctet5.Text = doctets[5];
                tB_DOctet6.Text = doctets[6];
                tB_DOctet7.Text = doctets[7];

                UpdateLayout();
            }
            else
            {
                SetIPAddressDelegate d = new SetIPAddressDelegate(SetIPAddress);
                Dispatcher.Invoke(DispatcherPriority.Normal, d, value);
            }
        }

        private void tB_DOctet_GotFocus(object sender, EventArgs e)
        {
            TextBox tB = (TextBox)sender;

            _tmp = tB.Text;

            tB.SelectAll();
        }

        private void tB_DOctet_LostFocus(object sender, EventArgs e)
        {
            TextBox tB = (TextBox)sender;

            if (String.IsNullOrEmpty(tB.Text))
            {
                tB.Text = "0";
                tB.Focus();
            }
        }

        private void tB_DOctet_TextChanged(object sender, EventArgs e)
        {
            TextBox tB = (TextBox)sender;

            tB.Text = tB.Text.ToLowerInvariant();

            if (String.IsNullOrEmpty(tB.Text))
            {
                _tmp = tB.Text;
                return;
            }

            UInt16 result;
            if (!UInt16.TryParse(tB.Text, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out result))
            {
                tB.Text = _tmp;
                tB.CaretIndex = tB.Text.Length;

                return;
            }

            _tmp = tB.Text;

            if (tB.CaretIndex == 4 && tB.Name != "tB_DOctet7")
            {
                tB.MoveFocus(new TraversalRequest(FocusNavigationDirection.Right));
            }

            IPAddress addr;
            if (tB_DOctet7 != null && IPAddress.TryParse(tB_DOctet0.Text + ":" +
                                                         tB_DOctet1.Text + ":" +
                                                         tB_DOctet2.Text + ":" +
                                                         tB_DOctet3.Text + ":" +
                                                         tB_DOctet4.Text + ":" +
                                                         tB_DOctet5.Text + ":" +
                                                         tB_DOctet6.Text + ":" +
                                                         tB_DOctet7.Text, out addr))
            {
                Address = addr;

                if (AddressChanged != null)
                {
                    AddressChanged(this, null);
                }
            }
        }

        private void tB_DOctet_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            TextBox tB = (TextBox)sender;

            if (e.Key == Key.Left && tB.CaretIndex == 0 && tB.Name != "tB_DOctet0")
            {
                tB.MoveFocus(new TraversalRequest(FocusNavigationDirection.Left));

                if (Keyboard.FocusedElement is TextBox)
                {
                    tB = (TextBox)Keyboard.FocusedElement;
                    tB.CaretIndex = tB.Text.Length;
                }

                e.Handled = true;

                return;
            }

            if (e.Key == Key.Right && tB.CaretIndex == tB.Text.Length && tB.Name != "tB_DOctet7")
            {
                tB.MoveFocus(new TraversalRequest(FocusNavigationDirection.Right));

                if (Keyboard.FocusedElement is TextBox)
                {
                    tB = (TextBox)Keyboard.FocusedElement;
                    tB.CaretIndex = 0;
                }

                e.Handled = true;

                return;
            }
        }

        private void tB_DOctet_PreviewTextInput(object sender, TextCompositionEventArgs e)
        {
            TextBox tB = (TextBox)sender;

            UInt16 res;
            if (!UInt16.TryParse(e.Text, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out res))
            {
                e.Handled = true;
            }

            base.OnPreviewTextInput(e);
        }
    }
}
